﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using VideoLink.Streams;
using VideoLink.Frames;

namespace VideoLink.Streams
{
    /// <summary>
    /// Источник видео, пересылающий видео единым файлом в первом пакете.
    /// Воспроизводит видео с помощью DirectShow
    /// </summary>
    public class EntireDSSource : ByFrameDSSource
    {
        #region Private fields

        /// <summary>
        /// Показывает, была ли нажата кнопка play хотя бы раз
        /// </summary>
        private bool playPressedOnce = false;
        /// <summary>
        /// Имя файла, который воспроизводится
        /// и который передаётся
        /// </summary>
        private string fileToSendName;

        /// <summary>
        /// Поток, из которого читаются фрагменты для передачи
        /// </summary>
        private FileStream fileToSend;
        /// <summary>
        /// Буфер, в который читаются фрагменты
        /// </summary>
        private byte[] buffer;
        /// <summary>
        /// Порядковый номер текущего фрагмента
        /// </summary>
        private int currentFragment;
        /// <summary>
        /// Всего фрагментов
        /// </summary>
        private int fragmentsCount;

        #endregion

        /// <summary>
        /// Создать экземпляр класса по умолчанию
        /// </summary>
        public EntireDSSource()
            : base()
        { 

        }

        /// <summary>
        /// Создать экземпляр класса с указанными параметрами
        /// </summary>
        /// <param name="streamID">ID потока, уникален среди всех потоков</param>
        /// <param name="fileName">Имя файла для воспроизведения</param>
        public EntireDSSource(int streamID, string fileName)
            : base(streamID, fileName)
        {
            fileToSendName = Path.GetTempFileName();
            fileToSendName = fileName;
            //File.Copy(fileName, fileToSendName, true);
        }

        /// <summary>
        /// Деструктор.
        /// </summary>
        ~EntireDSSource()
        {
            Close();
        }

        #region Public methods

        /// <summary>
        /// Получить данные, соответствующие данному кадру.
        /// </summary>
        /// <returns>
        /// При первом вызове возвращается VideoFragment с содержимым файла видео,
        /// при последующих вызовах работает следующая логика.
        /// Если State только что изменилось, 
        /// то используется EmptyFrame, иначе возвращается null
        /// </returns>
        public override VideoStreamState GetFrameData()
        {
            VideoStreamState result = null;
            //если ещё не всё отправили
            if (fileToSend != null)
            {
                result = GetNextFragment();
            }
            else if (needSendData)
            {
                result = new VideoStreamState(StreamID,
                    State,
                    new EmptyStream());

                //надо послать состояние после изменения State один раз и успокоиться
                needSendData = false;
            }

            return result;
        }
        
        /// <summary>
        /// Подготовить поток к закрытию (осободить все ресурсы).
        /// </summary>
        public override void Close()
        {
            base.Close();

            if (fileToSend != null)
                CloseStream();
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Play
        /// или при нажатии кнопки "Play" на контроле.
        /// Выполняются все действия, связанные с запуском воспроизведения
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnPlay(object sender, EventArgs e)
        {
            base.OnPlay(sender, e);
            if (!playPressedOnce)
            {
                playPressedOnce = true;
                State = PlaybackState.Stop;
                InitializeStream();
            }
        }

        #endregion

        #region File transmission

        /// <summary>
        /// Подготовиться к передаче видео по фрагментам
        /// </summary>
        private void InitializeStream()
        {
            buffer = new byte[VideoFragment.MaxFragmentLength];
            fileToSend = new FileStream(fileToSendName,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read);
            currentFragment = 0;
            fragmentsCount = (int)Math.Ceiling(fileToSend.Length * 1.0 / VideoFragment.MaxFragmentLength);
        }

        /// <summary>
        /// Получить следующий фрагмент.
        /// Если прочитан последний фрагмент, то поток закрывается.
        /// </summary>
        /// <returns></returns>
        private VideoStreamState GetNextFragment()
        {
            var bytesRead = fileToSend.Read(buffer,
                0,
                buffer.Length);

            var result = new VideoStreamState(StreamID,
                State,
                new VideoFragment(currentFragment,
                    fragmentsCount,
                    buffer));

            currentFragment++;

            if (bytesRead < buffer.Length)
            {
                CloseStream();
                State = PlaybackState.Play;
            }

            return result;
        }

        /// <summary>
        /// Закрыть поток, из которого берутся фрагменты
        /// </summary>
        private void CloseStream()
        {
            fileToSend.Close();
            fileToSend = null;

            buffer = null;
            
            //File.Delete(fileToSendName);
        }

        #endregion
    }
}
